package com.cvtt.xmpp;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;

import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.Roster.SubscriptionMode;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Presence.Type;
import org.jivesoftware.smack.packet.Privacy;
import org.jivesoftware.smack.packet.PrivacyItem;
import org.jivesoftware.smack.packet.RosterPacket.ItemType;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.provider.PrivacyProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smackx.PrivateDataManager;
import org.jivesoftware.smackx.bytestreams.socks5.provider.BytestreamsProvider;
import org.jivesoftware.smackx.packet.ChatStateExtension;
import org.jivesoftware.smackx.packet.DeliveryReceipt;
import org.jivesoftware.smackx.packet.DeliveryReceiptRequest;
import org.jivesoftware.smackx.packet.LastActivity;
import org.jivesoftware.smackx.packet.OfflineMessageInfo;
import org.jivesoftware.smackx.packet.OfflineMessageRequest;
import org.jivesoftware.smackx.packet.VCard;
import org.jivesoftware.smackx.provider.DataFormProvider;
import org.jivesoftware.smackx.provider.DelayInfoProvider;
import org.jivesoftware.smackx.provider.DeliveryReceiptProvider;
import org.jivesoftware.smackx.provider.DiscoverInfoProvider;
import org.jivesoftware.smackx.provider.DiscoverItemsProvider;
import org.jivesoftware.smackx.provider.MessageEventProvider;
import org.jivesoftware.smackx.provider.RosterExchangeProvider;
import org.jivesoftware.smackx.provider.StreamInitiationProvider;
import org.jivesoftware.smackx.provider.VCardProvider;
import org.jivesoftware.smackx.provider.XHTMLExtensionProvider;
import org.jivesoftware.smackx.pubsub.provider.EventProvider;
import org.jivesoftware.smackx.pubsub.provider.ItemProvider;
import org.jivesoftware.smackx.pubsub.provider.ItemsProvider;
import org.jivesoftware.smackx.pubsub.provider.PubSubProvider;
import org.jivesoftware.smackx.search.UserSearch;

import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.cvtt.common.Util;
import com.cvtt.xmpp.caps.CapsProvider;
import com.cvtt.xmpp.ping.PingExtension;
import com.cvtt.xmpp.ping.PingProvider;
import com.cvtt.xmpp.utils.PresenceType;

public class XMPPEngine {

	private static final String LOGTAG = "XMPPEngine";

	public final static int LOGIN_OK = 0;
	public final static int LOGIN_ERROR = 1;
	public final static int LOGIN_CONNECT_ERROR = 2;
	public final static int LOGIN_AUTH_ERROR = 3;

	public final static int STATUS_ONLINE = 10;
	public final static int STATUS_OFFLINE = 11;
	public final static int STATUS_KICK = 12;

	public final static int MSG_OK = 0;
	public final static int MSG_ERROR = 1;

	private static XMPPEngine xmppEngine = null;
	private XMPPConnectionAdapter connectionAdapter = null;
	private XMPPRosterListener rosterListener;
	private XMPPPacketListener packetListener;

	private Roster roster;
	private Collection<RosterEntry> xmppRosters;

	private Chat newMsg;

	// 发送心跳包的MsgID
	private String strPingID = "";
	// 心跳包丢包的数量
	private int nLostPingCount = 0;
	private int nReconnectCount = 0;
	private boolean bReconnect = false;

	private int nErrorCode = 0;
	private String strErrorMessage = "";

	private boolean xmppContactsLoaded = false;
	private boolean xmppContactsUpdated = false;

	private int nStatus;

	private boolean bReady = false;

	private XMPPListener xmppListener;

	public static synchronized XMPPEngine getInstance(Context ctx, XMPPListener listener) {
		if (xmppEngine == null) {
			xmppEngine = new XMPPEngine(ctx, listener);
		}
		return xmppEngine;
	}

	public static XMPPEngine getXMPPEngine() {
		return xmppEngine;
	}

	private XMPPEngine(Context ctx, XMPPListener listener) {
		if (XMPPConfig.LOG_ENABLE)
			Log.d(LOGTAG, "XMPPEngine()...");

		xmppListener = listener;

		initConfig();

		rosterListener = new XMPPRosterListener(this);
		packetListener = new XMPPPacketListener(this);

	}

	private void initConfig() {
		// Modified by huah in 2012-12-15
		SmackConfiguration.setKeepAliveInterval(-1);
		Roster.setDefaultSubscriptionMode(SubscriptionMode.manual); // 允许所有用户添加好友
		// configure(ProviderManager.getInstance());
		ProviderManager pm = ProviderManager.getInstance();
		pm.addIQProvider("query", "jabber:iq:private", new PrivateDataManager.PrivateDataIQProvider());
		try {
			pm.addIQProvider("query", "jabber:iq:time", Class.forName("org.jivesoftware.smackx.packet.Time"));
		}
		catch (ClassNotFoundException e) {
			Log.w(LOGTAG, "Can't load class for org.jivesoftware.smackx.packet.Time");
		}
		// Roster Exchange
		pm.addExtensionProvider("x", "jabber:x:roster", new RosterExchangeProvider());
		// Message Events
		pm.addExtensionProvider("x", "jabber:x:event", new MessageEventProvider());
		// XHTML
		pm.addExtensionProvider("html", "http://jabber.org/protocol/xhtml-im", new XHTMLExtensionProvider());
		// Data Forms
		pm.addExtensionProvider("x", "jabber:x:data", new DataFormProvider());
		// Service Discovery # Items
		pm.addIQProvider("query", "http://jabber.org/protocol/disco#items", new DiscoverItemsProvider());
		// Service Discovery # Info
		pm.addIQProvider("query", "http://jabber.org/protocol/disco#info", new DiscoverInfoProvider());
		// Privacy
		pm.addIQProvider("query", "jabber:iq:privacy", new PrivacyProvider());
		// Delayed Delivery only the new version
		pm.addExtensionProvider("delay", "urn:xmpp:delay", new DelayInfoProvider());
		pm.addExtensionProvider("received", DeliveryReceipt.NAMESPACE, new DeliveryReceiptProvider());
		// Service Discovery # Items
		pm.addIQProvider("query", "http://jabber.org/protocol/disco#items", new DiscoverItemsProvider());
		// Service Discovery # Info
		pm.addIQProvider("query", "http://jabber.org/protocol/disco#info", new DiscoverInfoProvider());
		// Chat State
		ChatStateExtension.Provider chatState = new ChatStateExtension.Provider();
		pm.addExtensionProvider("active", "http://jabber.org/protocol/chatstates", chatState);
		pm.addExtensionProvider("composing", "http://jabber.org/protocol/chatstates", chatState);
		pm.addExtensionProvider("paused", "http://jabber.org/protocol/chatstates", chatState);
		pm.addExtensionProvider("inactive", "http://jabber.org/protocol/chatstates", chatState);
		pm.addExtensionProvider("gone", "http://jabber.org/protocol/chatstates", chatState);
		// capabilities
		pm.addExtensionProvider("c", "http://jabber.org/protocol/caps", new CapsProvider());
		// Pubsub
		pm.addIQProvider("pubsub", "http://jabber.org/protocol/pubsub", new PubSubProvider());
		pm.addExtensionProvider("items", "http://jabber.org/protocol/pubsub", new ItemsProvider());
		pm.addExtensionProvider("items", "http://jabber.org/protocol/pubsub", new ItemsProvider());
		pm.addExtensionProvider("item", "http://jabber.org/protocol/pubsub", new ItemProvider());
		pm.addExtensionProvider("items", "http://jabber.org/protocol/pubsub#event", new ItemsProvider());
		pm.addExtensionProvider("item", "http://jabber.org/protocol/pubsub#event", new ItemProvider());
		pm.addExtensionProvider("event", "http://jabber.org/protocol/pubsub#event", new EventProvider());
		// TODO rajouter les manquants pour du full pubsub
		// ping
		pm.addIQProvider("ping", "urn:xmpp:ping", new PingProvider());
		// Version
		try {
			pm.addIQProvider("query", "jabber:iq:version", Class.forName("org.jivesoftware.smackx.packet.Version"));
		}
		catch (ClassNotFoundException e) {
			// Not sure what's happening here.
		}
		// VCard
		pm.addIQProvider("vCard", "vcard-temp", new VCardProvider());
		// Offline Message Requests
		pm.addIQProvider("offline", "http://jabber.org/protocol/offline", new OfflineMessageRequest.Provider());
		// Offline Message Indicator
		pm.addExtensionProvider("offline", "http://jabber.org/protocol/offline", new OfflineMessageInfo.Provider());
		// Last Activity
		pm.addIQProvider("query", "jabber:iq:last", new LastActivity.Provider());
		// User Search
		pm.addIQProvider("query", "jabber:iq:search", new UserSearch.Provider());
		// FileTransfer
		pm.addIQProvider("si", "http://jabber.org/protocol/si", new StreamInitiationProvider());
		pm.addIQProvider("query", "http://jabber.org/protocol/bytestreams", new BytestreamsProvider());
		// PEP avatar
		/**
		 * pm.addExtensionProvider("metadata", "urn:xmpp:avatar:metadata", new AvatarMetadataProvider()); pm.addExtensionProvider("data", "urn:xmpp:avatar:data", new AvatarProvider());
		 */
		pm.addIQProvider(PingExtension.ELEMENT, PingExtension.NAMESPACE, PingExtension.class);
	}

	public boolean start() {

		return true;

	}

	public void stop() {

		reset();
		disconnect();

	}

	public void reset() {
		resetAliveData();

		strPingID = "";

		nErrorCode = 0;
		strErrorMessage = "";

		xmppContactsLoaded = false;
		xmppContactsUpdated = false;

		nStatus = STATUS_OFFLINE;

		bReady = false;
	}

	public void setReady(boolean ready) {
		bReady = ready;
	}

	public boolean bReady() {
		return bReady;
	}

	public boolean isContactsLoaded() {
		return xmppContactsLoaded;
	}

	public boolean isContactsUpdated() {
		return xmppContactsUpdated;
	}

	public void updateStatus(int status) {
		nStatus = status;
		xmppListener.onStatusEvent(status);
		if (nStatus == STATUS_ONLINE)
			setPresence(PresenceType.AVAILABLE);
		else if (nStatus == STATUS_OFFLINE)
			setPresence(PresenceType.UNAVAILABLE);
	}

	public boolean isOnline() {
		return nStatus == STATUS_ONLINE;
	}

	public synchronized XMPPConnectionAdapter getConnectionAdapter() {
		if (connectionAdapter == null) {
			connectionAdapter = new XMPPConnectionAdapter(this);
			Log.i("xmpp", connectionAdapter.getXMPPConnection().getHost());
		}
		return connectionAdapter;
	}

	public void setLoginInfo(String userName, String password) {
		XMPPConfig.setUserName(userName);
		XMPPConfig.setPsd(password);

	}

	/**
	 * 连接XMPP服务器 返回值：true为连接成功，false为连接识别，可通过getLastError()获取错误码
	 * */
	public synchronized boolean connect() {

		boolean connectOK = false;

		XMPPConnectionAdapter xmppConnAdapter = getConnectionAdapter();

		try {
			if (xmppConnAdapter.isConnected()) {
				connectOK = true;
			}
			else {
				xmppConnAdapter.connect();
				if (xmppConnAdapter.isConnected()) {
					connectOK = true;
					packetListener.registerPacketListener(xmppConnAdapter.getXMPPConnection());
				}
			}
		}
		catch (XMPPException e) {
			XMPPError error = e.getXMPPError();
			if (error != null) {
				parseXMPPError(e);
			}
			else {
				nErrorCode = LOGIN_CONNECT_ERROR;
				strErrorMessage = "xmppError is null";
			}
			e.printStackTrace();
		}
		catch (Exception e) {
			nErrorCode = LOGIN_CONNECT_ERROR;
			strErrorMessage = e.toString();
			e.printStackTrace();
		}
		catch (Error e) {
			nErrorCode = LOGIN_CONNECT_ERROR;
			strErrorMessage = e.toString();
			e.printStackTrace();
		}
		if (!connectOK) {
			onLoginEvent(LOGIN_CONNECT_ERROR);
		}
		return connectOK;
	}

	public synchronized void disconnect() {

		if (XMPPConfig.LOG_ENABLE)
			Log.d(LOGTAG, "disconnect()...");

		updateStatus(STATUS_OFFLINE);

		try {
			XMPPConnectionAdapter xmppConnAdapter = getConnectionAdapter();
			packetListener.removePacketListener(xmppConnAdapter.getXMPPConnection());
			xmppConnAdapter.disconnect();
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		catch (Error e) {
			e.printStackTrace();
		}

		if (XMPPConfig.LOG_ENABLE)
			Log.d(LOGTAG, "after xmppEngine.getConnectionAdapter().disconnect()");

		connectionAdapter = null;
	}

	public void disconnectSync() {
		if (XMPPConfig.LOG_ENABLE)
			Log.d(LOGTAG, "disconnectSync()...");

		updateStatus(STATUS_OFFLINE);

		try {
			XMPPConnectionAdapter xmppConnAdapter = getConnectionAdapter();
			packetListener.removePacketListener(xmppConnAdapter.getXMPPConnection());
			xmppConnAdapter.disconnectSync();
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		catch (Error e) {
			e.printStackTrace();
		}

		if (XMPPConfig.LOG_ENABLE)
			Log.d(LOGTAG, "after xmppEngine.getConnectionAdapter().disconnect()");

		connectionAdapter = null;
	}

	public synchronized boolean login(String uNumber, String password) {

		nLostPingCount = 0;

		setLoginInfo(uNumber, password);

		return login();
	}

	public synchronized boolean login() {

		// added by huah in 2014-07-16 for debug
		if (isOnline())
			return true;

		int loginResult = LOGIN_ERROR;
		boolean loginOK = false;
		nLostPingCount = 0;
		if (!connect())
			return loginOK;
		XMPPConnectionAdapter xmppConnAdapter = getConnectionAdapter();
		try {
			loginOK = xmppConnAdapter.login(XMPPConfig.getUserName(), XMPPConfig.getPassWord());
		}
		catch (XMPPException e) {
			String errorReason = e.getLocalizedMessage();
			if ((errorReason != null) && errorReason.contains("SASL authentication failed using mechanism")) {
				loginResult = LOGIN_AUTH_ERROR;
				nErrorCode = LOGIN_AUTH_ERROR;
				strErrorMessage = errorReason;
			}
			else {
				XMPPError error = e.getXMPPError();
				if (error != null) {
					parseXMPPError(e);
				}
				else {
					nErrorCode = LOGIN_ERROR;
					strErrorMessage = "unknow xmpp exception";
				}
			}

			e.printStackTrace();

			loginOK = false;

		}
		catch (Exception e) {
			nErrorCode = LOGIN_ERROR;
			strErrorMessage = e.toString();

			e.printStackTrace();

			loginOK = false;
		}

		if (loginOK) {
			XMPPConnection xmppConn = xmppConnAdapter.getValidXMPPConnection();
			if (xmppConn == null) {
				nErrorCode = LOGIN_ERROR;
				strErrorMessage = "XMPPConnection is invalid!";
				loginOK = false;
			}

			if (!xmppConnAdapter.isAuthentificated()) {
				nErrorCode = LOGIN_ERROR;
				strErrorMessage = "XMPPConnection is invalid!";
				loginOK = false;
			}

			if (loginOK) {

				loginResult = LOGIN_OK;

				// denyPresenceIn();

				loadMyInfo();

				rosterListener = new XMPPRosterListener(this);

				roster = xmppConn.getRoster();

				if (roster != null) {
					roster.reload();
					roster.addRosterListener(rosterListener);
				}
			}
		}

		setReady(loginOK);

		onLoginEvent(loginResult);

		if (loginOK) {
			resetAliveData();
			keepAliveAfterTime(getPingPeriod());
			updateStatus(STATUS_ONLINE);
		}
		else
			updateStatus(STATUS_OFFLINE);

		return loginOK;
	}

	public synchronized void logout() {
		reset();
		disconnect();
	}

	public void resetAliveData() {
		nLostPingCount = 0;
		nReconnectCount = 0;
		bReconnect = false;
	}

	public int getPingPeriod() {
		return XMPPConfig.PING_PERIOD;
	}

	public int getReconnectPeriod() {
		if (nReconnectCount > 20) {
			return 120;
		}
		if (nReconnectCount > 10) {
			return 90;
		}
		return 60;
	}

	private boolean checkAlive() {
		if (getConnectionAdapter() == null) {
			nLostPingCount = nLostPingCount + XMPPConfig.MAX_LOST_PACKAGE_COUNT;
			return false;
		}
		if (!(getConnectionAdapter().isConnectionValid())) {
			nLostPingCount = nLostPingCount + XMPPConfig.MAX_LOST_PACKAGE_COUNT;
			return false;
		}
		if (nLostPingCount >= XMPPConfig.MAX_LOST_PACKAGE_COUNT) {
			return false;
		}
		return true;
	}

	private void keepAliveAfterTime(int time) {
		if (xmppListener != null)
			xmppListener.onKeepAlive(time);
	}

	// Added by huah in 2012-12-12
	public synchronized void keepAlive() {
		if (XMPPConfig.LOG_ENABLE) {
			Log.e(LOGTAG, "XMPPEngine.keepAlive() run...");
		}

		if (!checkAlive()) {
			bReconnect = true;
		}

		if (bReconnect) {
			disconnectSync();

			boolean loginOK = false;
			if (connect()) {
				if (bReady()) {
					loginOK = login();
				}
				else {
					loginOK = login();
				}
			}

			if (!loginOK) {
				nReconnectCount++;
				if (XMPPConfig.LOG_ENABLE)
					Log.e("nReconnectCount", "" + nReconnectCount);
				keepAliveAfterTime(getReconnectPeriod());
			}
		}
		else {
			sendPingPacket();
		}
	}

	public void reStart() {

		if (XMPPConfig.LOG_ENABLE)
			Log.v(LOGTAG, "reStart...");

		resetAliveData();

		boolean loginOK = false;

		if (connect()) {
			loginOK = login();
		}

		if (!loginOK)
			keepAliveAfterTime(xmppEngine.getReconnectPeriod());
	}

	public synchronized void reStart(String uNumber, String password) {
		setLoginInfo(uNumber, password);
		reStart();
	}

	// Added by huah in 2012-09-18
	private void loadMyInfo() {
		VCard myVCard = getUserVCard(null);
		if (myVCard != null) {
			if (xmppListener != null)
				xmppListener.onInfoLoaded(myVCard);
		}
	}

	// Added by huah in 2012-10-08
	public boolean loadXMPPInfo(String jid) {

		if (TextUtils.isEmpty(jid))
			return false;

		VCard userVCard = getUserVCard(jid);
		if (userVCard == null)
			return false;

		if (xmppListener != null)
			xmppListener.onContactInfoLoaded(jid, userVCard);

		return true;
	}

	private void onLoginEvent(int event) {
		if (xmppListener != null)
			xmppListener.onLoginEvent(event);
	}

	public String sendMessage(String toJID, Message msg) {
		String flag = "";
		if (TextUtils.isEmpty(toJID)) {
			return flag;
		}
		if (getConnectionAdapter() == null || getConnectionAdapter().getValidXMPPConnection() == null || getConnectionAdapter().getValidXMPPConnection().getChatManager() == null) {
			return flag;
		}
		if ((newMsg = getConnectionAdapter().getValidXMPPConnection().getChatManager().createChat(toJID, null)) == null) {
			return flag;
		}
		try {
			msg.addExtension(new DeliveryReceiptRequest());

			newMsg.sendMessage(msg);
			return msg.getPacketID();
		}
		catch (XMPPException e) {
			return flag;
		}
		catch (Exception e) {
			return flag;
		}
	}

	public void sendReceipt(String toJID, String id) {
		if (XMPPConfig.LOG_ENABLE)
			Log.d("", "sending XEP-0184 ack to " + toJID + " id=" + id);

		if (getConnectionAdapter() == null)
			return;

		if (getConnectionAdapter().getValidXMPPConnection() == null)
			return;

		org.jivesoftware.smack.packet.Message ack = new org.jivesoftware.smack.packet.Message(toJID, org.jivesoftware.smack.packet.Message.Type.normal);
		ack.addExtension(new DeliveryReceipt(id));
		getConnectionAdapter().getValidXMPPConnection().sendPacket(ack);
	}

	public void sendPingPacket() {
		PingExtension ping = new PingExtension();
		ping.setFrom(XMPPConfig.XMPP_USERNAME + "@" + XMPPConfig.XMPP_HOST + "/" + XMPPConfig.XMPP_RESOURCE_NAME);
		ping.setTo(XMPPConfig.XMPP_HOST);

		strPingID = ping.getPacketID();
		nLostPingCount++;

		// ping.setTo(from);
		if ((getConnectionAdapter().getValidXMPPConnection() != null) && getConnectionAdapter().getValidXMPPConnection().isConnected()) {
			getConnectionAdapter().getValidXMPPConnection().sendPacket(ping);
		}

		keepAliveAfterTime(getPingPeriod());
	}

	public void denyPresenceIn() {
		List<PrivacyItem> listPrivacy = new Vector<PrivacyItem>();

		PrivacyItem it = new PrivacyItem("jid", false, 0);
		it.setValue(XMPPConfig.XMPP_HOST);
		it.setFilterPresence_in(true);
		listPrivacy.add(it);

		Privacy request = new Privacy();
		request.setType(IQ.Type.SET);
		request.setFrom(XMPPConfig.XMPP_USERNAME + "@" + XMPPConfig.XMPP_HOST + "/" + XMPPConfig.XMPP_RESOURCE_NAME);
		request.setPrivacyList("presin-global", listPrivacy);

		Log.d(LOGTAG, request.toXML());
		getConnectionAdapter().getValidXMPPConnection().sendPacket(request);
	}

	/**
	 * 
	 * 设置心情和自己的presence状态，presenceType 可用com.cvtt.xmpp.utils.PresenceType中的相关方法
	 * */
	public void setPresence(int presenceType, String mood) {
		Presence presence = new Presence(PresenceType.getPresenceTypeFrom(presenceType));
		String status = mood;
		presence.setStatus(status);
		presence.setPriority(1);
		presence.setType(PresenceType.getPresenceTypeFrom(presenceType));

		if (getConnectionAdapter() == null)
			return;
		if (getConnectionAdapter().getValidXMPPConnection() == null)
			return;

		getConnectionAdapter().getValidXMPPConnection().sendPacket(presence);
	}

	public void setPresence(int presenceType) {
		Presence presence = new Presence(PresenceType.getPresenceTypeFrom(presenceType));
		presence.setPriority(1);
		presence.setType(PresenceType.getPresenceTypeFrom(presenceType));

		if (getConnectionAdapter() == null)
			return;
		if (getConnectionAdapter().getValidXMPPConnection() == null)
			return;

		getConnectionAdapter().getValidXMPPConnection().sendPacket(presence);
	}

	public Presence getUserPresence(String jid) {
		return roster.getPresence(jid);
	}

	/**
	 * 获取Vcard，传入null时获取自己的vcard扩展
	 * */
	public VCard getUserVCard(String jid) {

		if (getConnectionAdapter() == null)
			return null;
		if (getConnectionAdapter().getValidXMPPConnection() == null)
			return null;

		VCard vcard = new VCard();
		try {
			if ((jid != null) && (!(jid.trim().equals(""))))
				vcard.load(getConnectionAdapter().getValidXMPPConnection(), jid);
			else
				vcard.load(getConnectionAdapter().getValidXMPPConnection());
		}
		catch (XMPPException e) {
			XMPPError error = e.getXMPPError();
			if (error != null) {
				parseXMPPError(e);
			}
			return null;
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		return vcard;
	}

	/**
	 * 设置通用的 VCard字段. 当unescabale设置为 true时, 字段的值可以包含XML标签 如： setVcardField("PHOTO", "<TYPE>image/jpeg</TYPE><BINVAL>" + encodedImageData + "</BINVAL>", true); 如： setVcardField("AUTOROSTER", "1", false);
	 * 
	 * @param value
	 *            字段的值
	 * @param field
	 *            字段名
	 * @param isUnescapable
	 *            字段是否含标签<>
	 */
	public boolean setVCardField(String field, String value, boolean isUnescapable) {
		if (getConnectionAdapter() == null)
			return false;
		if (getConnectionAdapter().getXMPPConnection() == null)
			return false;
		boolean isOK = false;
		try {
			VCard vcard = new VCard();
			vcard.saveField(getConnectionAdapter().getXMPPConnection(), field, value, isUnescapable);
			isOK = true;
		}
		catch (Exception e) {
			isOK = false;
			e.printStackTrace();
		}
		catch (Error e) {
			isOK = false;
			e.printStackTrace();
		}
		return isOK;

	}

	/**
	 * 参数 isUnescapable 大部分为 false 为了方便起见，封装了这个函数
	 */
	public void setVcard(VCard vCard){
		try {
			vCard.save(getConnectionAdapter().getXMPPConnection());
		} catch (XMPPException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public boolean setVCardField(String field, String value) {
		return setVCardField(field, value, false);
	}

	public boolean setNickname(String nickname) {
		if (nickname == null)
			nickname = "";
		return setVCardField("NICKNAME", nickname);
	}

	public boolean setName(String name) {
		if (name == null)
			name = "";
		return setVCardField("NAME", name);
	}

	public boolean setPhoto(String photoData) {
		if (photoData == null)
			photoData = "";
		return setVCardField("PHOTO", "<TYPE>image/jpeg</TYPE><BINVAL>" + photoData + "</BINVAL>", true);
	}

	public boolean setMood(String mood) {
		if (mood == null)
			mood = "";
		return setVCardField("MOOD", mood);
	}

	public boolean setGender(String gender) {
		if (gender == null)
			gender = "";
		return setVCardField("GENDER", gender);
	}

	public boolean setBirthday(String birthday) {
		if (birthday == null)
			birthday = "";
		return setVCardField("BDAY", birthday);
	}

	public boolean setExtras(String extras) {
		if (extras == null)
			extras = "";
		return setVCardField("EXTRAS", extras);
	}

	public boolean setInfoPercent(String percent) {
		if (percent == null)
			percent = "";
		return setVCardField("INFO-PERCENT", percent);
	}

	public boolean setCheckContact(boolean checkContact) {
		int autoRoster = 1;
		if (checkContact)
			autoRoster = 0;
		return setVCardField("AUTOROSTER", "" + autoRoster);
	}

	/**
	 * 设置自己的VCard，如果觉得VCardEx格式国有复杂，可进一步封装
	 * 
	 * vcard.setField("MOOD","value") vcard.setField("AUTOROSTER","value")
	 * 
	 * */
	public void setNicknameAvatar(VCard vcard) {

		if (getConnectionAdapter() == null)
			return;
		if (getConnectionAdapter().getValidXMPPConnection() == null)
			return;

		try {
			vcard.save(getConnectionAdapter().getValidXMPPConnection());
		}
		catch (XMPPException e) {
			XMPPError error = e.getXMPPError();
			if (error != null) {
				parseXMPPError(e);
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 设置昵称、头像、心情
	 * */
	public boolean setNicknameAvatarMood(String nickName, String encodedAvatarImage, String mood) {
		boolean bFlag = false;
		if (getConnectionAdapter() == null)
			return false;
		if (getConnectionAdapter().getValidXMPPConnection() == null)
			return false;
		try {
			VCard vcard = new VCard();
			vcard.load(getConnectionAdapter().getValidXMPPConnection());
			vcard.setNickName(vcard.getNickName());

			// vcard.setField("PHOTO", "<TYPE>image/jpg</TYPE><BINVAL></BINVAL>", true);
			if (mood != null)
				vcard.setField("MOOD", mood);
			if (nickName != null)
				vcard.setNickName(nickName);
			if (encodedAvatarImage != null) {
				vcard.setField("PHOTO", "<TYPE>image/jpg</TYPE><BINVAL>" + encodedAvatarImage + "</BINVAL>", true);
			}
			else {
				byte[] photo = vcard.getAvatar();
				vcard.setAvatar(photo);
			}

			vcard.save(getConnectionAdapter().getValidXMPPConnection());
			bFlag = true;
		}
		catch (XMPPException e) {
			e.printStackTrace();
			XMPPError error = e.getXMPPError();
			if (error != null) {
				parseXMPPError(e);
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		return bFlag;
	}

	/**
	 * 设置是否运行对方添加好友，0为不允许1为运行
	 * */
	public void setAutoroster(int Autoroster) {

		if (getConnectionAdapter() == null)
			return;
		if (getConnectionAdapter().getValidXMPPConnection() == null)
			return;

		try {
			VCard vcard = new VCard();
			vcard.load(getConnectionAdapter().getValidXMPPConnection());
			byte[] photo = vcard.getAvatar();
			vcard.setAvatar(photo);
			vcard.setField("AUTOROSTER", "" + Autoroster);
			vcard.save(getConnectionAdapter().getValidXMPPConnection());
		}
		catch (XMPPException e) {
			XMPPError error = e.getXMPPError();
			if (error != null) {
				parseXMPPError(e);
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 设置是否把离线消息转为短信，0为不转换 1为需要转
	 * */
	public void setAutoSMS(int AutoSms) {
		if (getConnectionAdapter() == null)
			return;
		if (getConnectionAdapter().getValidXMPPConnection() == null)
			return;

		try {
			VCard vcard = new VCard();
			vcard.load(getConnectionAdapter().getValidXMPPConnection());
			vcard.setField("AUTOSMS", "" + AutoSms);
			byte[] photo = vcard.getAvatar();
			vcard.setAvatar(photo);
			vcard.save(getConnectionAdapter().getValidXMPPConnection());
		}
		catch (XMPPException e) {
			XMPPError error = e.getXMPPError();
			if (error != null) {
				parseXMPPError(e);
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	public boolean addContact(String username, String remark) {
		if (getConnectionAdapter() == null)
			return false;
		if (getConnectionAdapter().getValidXMPPConnection() == null)
			return false;

		boolean addfriendFlag = false;
		try {
			if (TextUtils.isEmpty(remark))
				roster.createEntry(username + "@" + XMPPConfig.XMPP_HOST, username, new String[] { "Friends" });
			else
				roster.createEntryWithWords(username + "@" + XMPPConfig.XMPP_HOST, username, new String[] { "Friends" }, remark);
			addfriendFlag = true;
		}
		catch (XMPPException e) {
			XMPPError error = e.getXMPPError();
			if (error != null) {
				parseXMPPError(e);
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		return addfriendFlag;
	}

	public boolean acceptContact(String jid) {
		Presence presenceAdd = new Presence(Type.subscribed);
		presenceAdd.setTo(jid);
		presenceAdd.setFrom(XMPPConfig.getUserName() + "@" + XMPPConfig.XMPP_HOST);
		getConnectionAdapter().getValidXMPPConnection().sendPacket(presenceAdd);

		String xmppID = Util.getStartString(jid, "@");
		return addContact(xmppID, null);
	}

	/**
	 * 删除好友操作
	 */
	// TODO:by huah in 2012-12-26 ???
	public boolean deleteContact(String jid) {
		boolean deleteFlag = false;
		try {
			// TODO:
			// DataLogic.getInstance().sendMessageDeley(DataLogic.WHAT_DEL_XMPP_CONTACT, jid, 0);
			RosterEntry entry = roster.getEntry(jid);
			if (entry != null)
				roster.removeEntry(entry);
			deleteFlag = true;
		}
		catch (XMPPException e) {
			XMPPError error = e.getXMPPError();
			if (error != null) {
				parseXMPPError(e);
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		return deleteFlag;
	}

	/*public void onContactUpdated(String jid) {
		String myUNumber = XMPPConfig.getUserName();
		String uNumber = UUtil.getUNumber(jid);
		if (TextUtils.isEmpty(uNumber) || TextUtils.equals(myUNumber, uNumber))
			return;
		RosterEntry rosterEntry = getRosterEntry(jid);
		if (rosterEntry == null)
			return;
		if (XMPPConfig.LOG_ENABLE)
			Log.d("onContactAdded", rosterEntry.toString());

		if (rosterEntry.getType().equals(ItemType.both)) {
			if (xmppListener != null)
				xmppListener.onContactAdded(rosterEntry);
		}
		else {
			if (xmppListener != null)
				xmppListener.onContactDeleted(jid);
		}
	}*/

	/*public void onContactDeleted(String jid) {
		String myUNumber = XMPPConfig.getUserName();
		String uNumber = UUtil.getUNumber(jid);
		if (TextUtils.isEmpty(uNumber) || TextUtils.equals(myUNumber, uNumber))
			return;
		// RosterEntry rosterEntry = getRosterEntry(jid);
		// if(rosterEntry == null)
		// return;
		if (XMPPConfig.LOG_ENABLE)
			Log.d("onContactDeleted", jid);
		if (xmppListener != null)
			xmppListener.onContactDeleted(jid);
	}*/

	public void onConnectionReplaced() {
		updateStatus(STATUS_KICK);
	}

	/**
	 * 解析xmppexception
	 * 
	 * @param exception
	 */
	private void parseXMPPError(XMPPException exception) {
		XMPPError error = exception.getXMPPError();
		if (error != null) {
			nErrorCode = error.getCode();
			strErrorMessage = error.getMessage();
		}
	}

	/**
	 * field 支持以下字段 user nick email
	 */
	public boolean checkUser(String field, String value) {
		return true;
		// boolean bFlag = false;
		//
		// if (getConnectionAdapter() == null)
		// return false;
		// if (getConnectionAdapter().getValidXMPPConnection() == null)
		// return false;
		//
		// try {
		//
		// UserSearchManager search = new UserSearchManager(getConnectionAdapter().getValidXMPPConnection());
		// Form searchForm = search.getSearchForm(XMPPConfig.SEARCH_XMPP_HOST);
		// Form answerForm = searchForm.createAnswerForm();
		// answerForm.setAnswer(field, value + "*");
		// ReportedData data = search.getSearchResults(answerForm, XMPPConfig.SEARCH_XMPP_HOST);
		// // Use Returned Data
		//
		// Iterator<Row> it = data.getRows();
		// while (it.hasNext()) {
		// Row row = it.next();
		// if (XMPPConfig.LOG_ENABLE) {
		// Log.e("jid", row.getValues("jid").next().toString());
		// Log.e("email", row.getValues("email").next().toString());
		// Log.e("nick", row.getValues("nick").next().toString());
		// }
		//
		// bFlag = true;
		// }
		// }
		// catch (XMPPException e) {
		// parseXMPPError(e);
		// return bFlag;
		// }
		// return bFlag;
	}

	public void processRosterPacket(Packet packet) {
		if (roster != null) {
			roster.processRosterPacket(packet);
			loadXMPPContacts();
		}
	}

	public void processSubscribePacket(Packet packet) {

		/*String from = packet.getFrom();
		String remark = PresenceType.getStatus(packet.toXML());

		RosterEntry rosterEntry = XMPPEngine.getXMPPEngine().getRosterEntry(from);

		if ((rosterEntry == null) || rosterEntry.getType().equals(ItemType.none)) {
			Presence presenceUn = new Presence(Type.unsubscribe);
			presenceUn.setTo(from);
			presenceUn.setFrom(XMPPConfig.getUserName() + "@" + XMPPConfig.XMPP_HOST);
			getConnectionAdapter().getValidXMPPConnection().sendPacket(presenceUn);

			if (xmppListener != null) {
				xmppListener.onAddContact(from, remark);
			}
		}
		else if (rosterEntry.getType().equals(ItemType.to)) {
			Presence presenceAdd = new Presence(Type.subscribed);
			presenceAdd.setTo(from);
			presenceAdd.setFrom(XMPPConfig.getUserName() + "@" + XMPPConfig.XMPP_HOST);
			getConnectionAdapter().getValidXMPPConnection().sendPacket(presenceAdd);
		}
		else {
			acceptContact(from);
		}*/
	}

	// Added by huah in 2012-12-22
	public void processMessagePacket(Packet packet) {

		org.jivesoftware.smack.packet.Message msg = (org.jivesoftware.smack.packet.Message) packet;

		if (msg.getType() == org.jivesoftware.smack.packet.Message.Type.error)
			return;

		HashMap<String, String> map;
		DeliveryReceipt dr = (DeliveryReceipt) msg.getExtension("received", DeliveryReceipt.NAMESPACE);
		if (dr != null) {
			// TODO:
			// map = new HashMap<String, String>(2);
			// map.put("packetid", dr.getId());
			// // 普通消息只有成功才有回执码
			// map.put("send_status", "0"); // 0表示成功；1表示失败

			if (xmppListener != null)
				xmppListener.onMessageEvent(dr.getId(), MSG_OK);

			if (XMPPConfig.LOG_ENABLE)
				Log.e(LOGTAG, "got delivery receipt for " + dr.getId());
			return;
		}
		if (msg.getExtension("request", DeliveryReceipt.NAMESPACE) != null) {
			sendReceipt(msg.getFrom(), msg.getPacketID());
		}

		if (xmppListener != null)
			xmppListener.onRecvMessage(msg);
	}

	public void processOtherPacket(Packet packet) {
		// XMPP 检测断线
		if (strPingID.equals(packet.getPacketID())) {
			nLostPingCount = 0;
		}
		// 响应server的ping消息
		if ((packet.toXML().contains("<ping")) && (packet.toXML().contains("get"))) {
			PingExtension ping = new PingExtension();
			ping.setPacketID(packet.getPacketID());
			ping.setType(org.jivesoftware.smack.packet.IQ.Type.RESULT);
			ping.setFrom(XMPPConfig.XMPP_USERNAME + "@" + XMPPConfig.XMPP_HOST + "/" + XMPPConfig.XMPP_RESOURCE_NAME);
			ping.setTo(XMPPConfig.XMPP_HOST);

			if ((getConnectionAdapter().getXMPPConnection() != null) && getConnectionAdapter().getXMPPConnection().isConnected()) {
				getConnectionAdapter().getXMPPConnection().sendPacket(ping);
			}

			nLostPingCount = 0;
		}
	}

	// Added by huah in 2012-11-07
	public RosterEntry getRosterEntry(String userURI) {
		if (TextUtils.isEmpty(userURI))
			return null;
		roster = getConnectionAdapter().getValidXMPPConnection().getRoster();
		if (roster == null)
			return null;
		Collection<RosterEntry> it = roster.getEntries();

		String rosterUser;
		for (RosterEntry rosterEntry : it) {
			rosterUser = rosterEntry.getUser();
			if (TextUtils.equals(userURI, rosterUser))
				return rosterEntry;
		}
		return null;
	}

	public synchronized void loadXMPPContacts() {

		if (xmppContactsLoaded)
			return;

		XMPPConnectionAdapter xmppConnAdapter = getConnectionAdapter();
		if (xmppConnAdapter == null)
			return;
		XMPPConnection xmppConn = xmppConnAdapter.getValidXMPPConnection();
		if (xmppConn == null)
			return;

		if (roster == null) {
			Log.v(LOGTAG, "loadXmppContacts:roster == null...1");
			roster = xmppConn.getRoster();
			roster.reload();
		}

		if (XMPPConfig.LOG_ENABLE)
			Log.v(LOGTAG, "loadXMPPContacts,roster.getEntryCount() = " + roster.getEntryCount());

		xmppRosters = roster.getEntries();

		if (xmppRosters == null || xmppRosters.size() < 1) {
			if (XMPPConfig.LOG_ENABLE)
				Log.d(LOGTAG, "Something is wrong,get 0 xmpp contacts!!!");
			return;
		}
		xmppContactsLoaded = true;
		if (xmppListener != null)
			xmppListener.onContactsLoaded(xmppRosters);
	}

	public Collection<RosterEntry> getXMPPRosters() {
		if (!xmppContactsLoaded)
			loadXMPPContacts();
		return xmppRosters;
	}

	public int getLastErrorCode() {
		return nErrorCode;
	}

	public String getLastErrorMessage() {
		return strErrorMessage;
	}
}
